# @Time : 2025-01-10 18:15
# @Author : Fioman 
# @Phone : 13149920693
"""
zmc的基础类,主要用来配置一些公用的一些函数库
"""
from ctypes import *

import gevent

import app_config
from common.exception_helper import error_handler_produce, show_error_info
from common.produce_logger import debug_produce
from personal_tool.data_result import DataResult

tryAgainDuration = 1000
zmcApi: CDLL | None = None


@error_handler_produce
def load_zmc_dll() -> DataResult:
    """
    加载api
    :return:
    """
    global zmcApi
    result = DataResult()
    zmcApi = cdll.LoadLibrary(app_config.AuxZmcLibPath)
    if zmcApi is not None:
        result.state = True
    else:
        result.info = "zmc驱动加载失败"
    return result

def get_modbus_bit(handler, address: int) -> DataResult:
    """
    获取modbus_bit的值,返回一个值0或者1,从起始位置count个数的modbus_bit的值
    :param handler:
    :param address: 起始位置
    :return: 返回一个bool
    """
    result = DataResult()
    value = (c_uint8 * 1)
    zmcApi.ZAux_Modbus_Get0x.restype = c_int32
    zmcApi.ZAux_Modbus_Get0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)
    res = zmcApi.ZAux_Modbus_Get0x(handler, address, 1, value)
    if res == 0:
        result.state = True
        result.data = value[:][0]
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Modbus_Get0x(handler, address, 1, value)
        if res == 0:
            result.state = True
            result.data = value[:][0]
        else:
            result.info = f"获取modbus_bit({address}) 的值失败,错误码: {res}"
    return result

# noinspection PyTypeChecker
@error_handler_produce
def get_modbus_bit_multi(handler, start: int, count: int) -> DataResult:
    """
    获取modbus_bit 位寄存器 的值,返回的是一个列表,从起始位置count个数的modbus_bit的值
    :param handler:
    :param start: 起始位置
    :param count: 需要获取的个数
    :return: 返回一个列表,包含所有获取到的值
    """
    result = DataResult()
    value = (c_uint8 * count)
    zmcApi.ZAux_Modbus_Get0x.restype = c_int32
    zmcApi.ZAux_Modbus_Get0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)
    res = zmcApi.ZAux_Modbus_Get0x(handler, start, count, value)
    if res == 0:
        result.state = True
        result.data = value[:]
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Modbus_Get0x(handler, start, count, value)
        if res == 0:
            result.state = True
            result.data = value[:]
        else:   
            result.info = f"获取 modbus_bit({start}, {count}) 的值失败,错误码: {res}"
    return result

# noinspection PyTypeChecker
@error_handler_produce
def get_modbus_float(handler, address: int) -> DataResult:
    """
    获取modbus_float的值,返回的是一个float,从起始位置count个数的modbus_float的值
    :param handler:
    :param address: 起始位置
    :return: 返回一个float
    """
    result = DataResult()
    value = (c_float * 1)
    zmcApi.ZAux_Modbus_Get4x_Float.restype = c_int32
    zmcApi.ZAux_Modbus_Get4x_Float.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)
    res = zmcApi.ZAux_Modbus_Get4x_Float(handler, address, 1, value)
    if res == 0:
        result.state = True
        result.data = value[:][0]
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Modbus_Get4x_Float(handler, address, 1, value)
        if res == 0:
            result.state = True
            result.data = value[:][0]
        else:
            result.info = f"获取modbus_float({address}) 的值失败,错误码: {res}"
    return result

# noinspection PyTypeChecker
@error_handler_produce
def get_modbus_float_multi(handler, start: int, count: int) -> DataResult:
    """
    获取modbus_float的值,返回的是一个列表,从起始位置count个数的modbus_float的值
    :param handler:
    :param start: 起始位置
    :param count: 需要获取的个数
    :return: 返回一个列表,包含所有获取到的值
    """
    result = DataResult()
    value = (c_float * count)
    zmcApi.ZAux_Modbus_Get4x_Float.restype = c_int32
    zmcApi.ZAux_Modbus_Get4x_Float.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)
    res = zmcApi.ZAux_Modbus_Get4x_Float(handler, start, count, value)
    if res == 0:
        result.state = True
        result.data = value[:]
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Modbus_Get4x_Float(handler, start, count, value)
        if res == 0:
            result.state = True
            result.data = value[:]
        else:
            result.info = f"获取 modbus_float({start}, {count}) 的值失败,错误码: {res}"
    return result


@error_handler_produce
def get_output_state(handler, output: int) -> DataResult:
    """
    获取输出状态,如果返回成功,data = True表示打开, data = False表示关闭
    :param handler:
    :param output:
    :return:
    """
    result = DataResult()
    value = c_int32(0)
    zmcApi.ZAux_Direct_GetOp.restype = c_int32
    zmcApi.ZAux_Direct_GetOp.argtype = (c_void_p, c_int32, POINTER(c_int32))
    res = zmcApi.ZAux_Direct_GetOp(handler, output, value)
    if res == 0:
        result.state = True
        result.data = True if value.value == 1 else False
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Direct_GetOp(handler, output, value)
        if res == 0:
            result.state = True
            result.data = True if value.value == 1 else False
        else:
            result.info = f"获取输出状态({output}) 的值失败,错误码: {res}"
    return result


@error_handler_produce
def get_in_state(handler, ioNum: int) -> DataResult:
    """
    获取输入状态,如果返回成功,data = True表示打开, data = False表示关闭
    :param handler:
    :param ioNum:
    :return:
    """
    result = DataResult()
    value = c_int32(0)
    zmcApi.ZAux_Direct_GetIn.restype = c_int32
    zmcApi.ZAux_Direct_GetIn.argtype = (c_void_p, c_int32, POINTER(c_int32))
    res = zmcApi.ZAux_Direct_GetIn(handler, ioNum, value)
    if res == 0:
        result.state = True
        result.data = True if value.value == 1 else False
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Direct_GetIn(handler, ioNum, value)
        if res == 0:
            result.state = True
            result.data = True if value.value == 1 else False
        else:
            result.info = f"获取输入状态({ioNum}) 的值失败,错误码: {res}"
    return result


# noinspection PyTypeChecker
@error_handler_produce
def get_in_state_multi(handler, start: int, count: int) -> DataResult:
    """
    获取多少个输入口的状态,返回的是一个列表,列表里是一个32位的整数,
    这个整数的每一位表示一个值.
    所以这里返回值也是一个列表,列表的大小就是end-start. 
    这个值怎么获取呢,就是通过将整数转换为二进制的数即可.
    结果也是True和False来表示,True表示打开,False表示关闭
    :param handler:
    :param start:
    :param count:
    :return:
    """
    result = DataResult()
    # 返回的值的个数,是count有多少个32
    listCount = int(count / 32 + 0.5)
    # 返回的默认值
    resListVal = [0] * count
    value = (c_uint32 * listCount)
    zmcApi.ZAux_Direct_GetInMulti.restype = c_int32
    zmcApi.ZAux_Direct_GetInMulti.argtype = (c_void_p, c_int32, c_int32, c_void_p)
    # 参数解释: start起始io, end结束io, value返回的值
    end = start + count
    res = zmcApi.ZAux_Direct_GetInMulti(handler, start, end, value)
    if res != 0:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Direct_GetInMulti(handler, start, end, value)
        if res != 0:
            result.info = f"获取输入状态({start}, {count}) 的值失败,错误码: {res}"
            return result

    for index, valInt in enumerate(value[:]):
        # 获取其二进制的数据,是字符串,并且返回的是倒序的,因为大小端的关系
        valBins = list(bin(valInt).replace("0b", ''))[::-1]
        for indexInner, valBin in enumerate(valBins):
            resListVal[index * 32 + indexInner] = True if valBin == '1' else False
    result.state = True
    result.data = resListVal
    return result


@error_handler_produce
def set_modbus_byte(handler, address: int, value: int) -> DataResult:
    """
    设置modbus_byte的值
    :param handler:
    :param address:
    :param value:
    :return:
    """
    result = DataResult()
    zmcApi.ZAux_Modbus_Set4x.restype = c_int32
    zmcApi.ZAux_Modbus_Set4x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)
    res = zmcApi.ZAux_Modbus_Set4x(handler, address, 1, value)
    if res == 0:
        result.state = True
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Modbus_Set4x(handler, address, 1, value)
        if res == 0:
            result.state = True
        else:
            result.info = f"设置modbus_byte({address}, {value}) 的值失败,错误码: {res}"
    return result


def set_modbus_string(self, msg):
    """
    设置modbus_string的值
    :param msg:发送的消息
    """
    maxStrLen = 12
    newMsg = msg
    if len(msg) < maxStrLen:
        n = int((maxStrLen - len(msg)) // 2)
        newMsg = " " * n + msg
    for i in range(0, maxStrLen, 2):
        try:
            b = newMsg[i]
        except Exception as e:
            b = " "
            debug_produce(f"newMsg[{i}] 获取失败,错误码: {e}")

        try:
            a = newMsg[i + 1]
        except Exception as e:
            a = " "
            debug_produce(f"newMsg[{i + 1}] 获取失败,错误码: {e}")

        self.set_modbus_byte(1010 + int(i / 2), (ord(a) << 8) | ord(b))


# noinspection PyTypeChecker
def set_table(handler, start: int, count: int, tableValue: list) -> DataResult:
    """
    设置table的值
    :param handler:
    :param start:
    :param count:
    :param tableValue:
    :return:
    """
    result = DataResult()
    value = (c_float * count)
    value[:] = tableValue
    zmcApi.ZAux_Direct_SetTable.restype = c_int32
    zmcApi.ZAux_Direct_SetTable.argtype = (c_void_p, c_uint32, c_uint32, c_void_p)
    res = zmcApi.ZAux_Direct_SetTable(handler, start, count, value)
    if res == 0:
        result.state = True
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Direct_SetTable(handler, start, count, value)
        if res == 0:
            result.state = True
        else:
            result.info = f"设置table({start}, {count}) 的值失败,错误码: {res}"
    return result

@error_handler_produce
def connect_zmc(handler,ip:string) -> DataResult:
    """
    打开zmc连接
    :param handler:
    :param ip:
    :return:
    """
    result = DataResult()
    if isinstance(ip,str):
        ip = ip.encode('ascii')
    zmcApi.ZMC_OpenEth.restype = c_int64
    zmcApi.ZMC_OpenEth.argtype = (c_char_p, c_void_p)
    res = zmcApi.ZMC_OpenEth(ip, handler)   
    if res == 0:
        result.state = True
    else:
        result.info = f"打开zmc连接失败,错误码: {res}"
    return result

@error_handler_produce
def close_zmc(handler):
    """
    关闭zmc连接
    :param handler:
    :return:
    """
    result = DataResult()
    zmcApi.ZAux_Close.restype = c_int32
    zmcApi.ZAux_Close.argtype = c_void_p
    status = zmcApi.ZAux_Close(handler)
    if status == 0:
        result.state = True
    else:
        gevent.sleep(tryAgainDuration)
        status = zmcApi.ZAux_Close(handler)
        if status == 0:
            result.state = True
        else:
            result.info = f"关闭zmc连接失败,错误码: {status}"
    return result


# noinspection PyTypeChecker
@error_handler_produce
def set_modbus_bit(handler, address: int, value: int) -> DataResult:
    """
    设置modbus_bit的值
    :param handler:
    :param address:
    :param value:
    :return:
    """
    result = DataResult()
    value = (c_uint8 * 1)
    zmcApi.ZAux_Modbus_Set0x.restype = c_int32
    zmcApi.ZAux_Modbus_Set0x.argtype = (c_void_p, c_uint16, c_uint16, c_void_p)
    res = zmcApi.ZAux_Modbus_Set0x(handler, address, 1, value)
    if res == 0:
        result.state = True
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_Modbus_Set0x(handler, address, 1, value)
        if res == 0:
            result.state = True
        else:
            result.info = f"设置modbus_bit({address}, {value}) 的值失败,错误码: {res}"
    return result


# noinspection PyTypeChecker
@error_handler_produce
def get_axis_coordinate(handler, axisCount: int) -> DataResult:
    """
    快速读取多个当前的 DPOS。 返回对应轴列表坐标
    :param handler:
    :param axisCount:轴数量
    :return:
    """
    result = DataResult()
    value = (c_float * axisCount)
    zmcApi.ZAux_GetModbusDpos.restype = c_int32
    zmcApi.ZAux_GetModbusDpos.argtype = (c_void_p, c_int32, c_void_p)
    res = zmcApi.ZAux_GetModbusDpos(handler, axisCount, value)
    if res == 0:
        result.state = True
        result.data = value[:]
    else:
        gevent.sleep(tryAgainDuration)
        res = zmcApi.ZAux_GetModbusDpos(handler, axisCount, value)
        if res == 0:
            result.state = True
            result.data = value[:]
        else:
            result.info = f"获取轴坐标失败,错误码: {res}"
    return result


def check_connect_zmc(handler) -> DataResult:
    """
    检测zmc是否在线
    @param handler:
    @return:
    """
    return get_modbus_bit(handler, 0)


def start_zmc(handler) -> DataResult:
    """
    zmc设备启动 ,所有的设备启动的位置都是0,设置的值为1
    @param handler:
    @return:
    """
    writePos, writeVal = 0, 1
    return set_modbus_bit(handler, writePos, writeVal)


def stop_zmc(handler) -> DataResult:
    """
    zmc设备停止,所有的设备停止的位置都是1,设置的值为1
    @param handler:
    @return:
    """
    writePos, writeVal = 1, 1
    return set_modbus_bit(handler, writePos, writeVal)

def rs_stop(handler) -> DataResult:
    """
    zmc设备停止,所有的设备停止的位置都是1,设置的值为1
    @param handler:
    @return:
    """
    writePos, writeVal = 2, 1
    return set_modbus_bit(handler, writePos, writeVal)

def reset_zmc(handler) -> DataResult:
    """
    zmc设备复位,所有的设备复位的位置都是1,设置的值为1
    @param handler:
    @return:
    """
    writePos, writeVal = 3, 1
    return set_modbus_bit(handler, writePos, writeVal)  

def is_run_state(handler) -> DataResult:
    """
    获取zmc设备运行状态
    @param handler:
    @return:
    """
    bitPos = 100
    return get_modbus_bit(handler, bitPos)

def is_stop_state(handler) -> DataResult:
    """
    获取zmc设备停止状态
    @param handler:
    @return:
    """
    bitPos = 101
    return get_modbus_bit(handler, bitPos)  

def is_rs_stop_state(handler) -> DataResult:
    """
    获取zmc设备停止状态
    @param handler:
    @return:
    """
    bitPos = 102
    return get_modbus_bit(handler, bitPos)  

def is_reset_state(handler) -> DataResult:
    """
    获取zmc设备复位状态
    @param handler:
    @return:
    """
    bitPos = 103
    return get_modbus_bit(handler, bitPos)  






if __name__ == '__main__':
    res = load_zmc_dll()
    if res.state:
        print(res.data)
    else:
        show_error_info(res.info)
